<!DOCTYPE html>
<html>
<head>
    <title>Closure</title>
</head>

<body>
	<ul>
		<li>111</li>
		<li>111</li>
		<li>111</li>
	</ul>
	<div id="div1">click me</div>
<script>


// 函数嵌套函数，内部函数可以引用外部函数的参数和变量，这些被引用的参数和变量不会被垃圾回收机制收回

/*function aaa(a){

	var b = 5;
	function bbb(){
		alert(a);
		alert(b);
	}
}
aaa();*/


// js中的垃圾回收机制，执行完后，变量a会被垃圾回收
/*function aaa(){
	var a = 1;
}
aaa();*/


// 闭包的形式
/*function aaa(){

	var a = 1;
	function bbb(){
		alert(a);
	}
	return bbb;
}
var c = aaa();
c();
*/



// ------------------------------------------------------------------------
// 闭包的好处：
// 1、希望一个变量长期驻扎在内存中
// 2、避免全局变量的污染
// 3、私有成员的存在

// 用法：
// 1、模块化代码
// 2、在循环中直接找到对应元素的索引


// 方法一、出现了全局变量a
/*var a = 1;
function aaa(){
	a++;
	alert(a);
}
aaa();  //2
aaa();  //3
*/

// 方法二、虽然消除了全局变量，但是每次执行变量a都是从1开始计数
/*function aaa(){
	var a = 1;
	a++;
	alert(a);
}
aaa();  //2
aaa();  //2
*/


// 方法三、使用闭包，解决问题

/*function aaa(){
	var a = 1;
	return function(){
		a++;
		alert(a);
	}
}
var b = aaa();
b();   //2
b();   //3
alert(a);  //error，外部访问不到a
*/


// 方法四、使用函数的自执行，改写方法三的函数
/*var aaa = (function(){
	var a = 1;
	return function(){
		a++;
		alert(a);
	}
})();

aaa();   //2   
aaa();   //3
*/




// 函数的模块化， 方法bbb ccc是内部的私有方法，变量a是内部的局部变量，在外部不能访问
/*var aaa = (function(){
	var a = 1;
	function bbb(){
		a++;
		alert(a);
	}
	function ccc(){
		a++;
		alert(a);
	}
	return {
		b : bbb,
		c : ccc
	}
})();

aaa.b();   //2
aaa.c();   //3
alert(a) // error
alert(bbb) // error
alert(ccc) // error*/



// 在循环中直接找到对应元素的索引
/*window.onload = function(){

	var aLi = document.getElementsByTagName("li");
	for(var i=0; i<aLi.length; i++){

		// 3,3,3...  因为外部循环在执行的时候，内部onclick事件并没有执行，只有去点击的时候，才执行，而此时for循环已经结束了，i就是3
		aLi[i].onclick = function(){
			alert(i);  
		};


		// onclick事件套一层，把循环中的i(0,1,2...)当做参传进去，在闭包中，内部函数可以调用外部函数的参数和变量，而外部函数执行完毕也不会影响内部函数；
		// 所以就可以利用闭包的形式，把0,1,2...传进去，贮存在内存中，我们想用的时候就直接来用就行了；
		// 方法一：
		(function(i){
			aLi[i].onclick = function(){
				alert(i);  
			};
		})(i);


		// 方法二：
		aLi[i].onclick = (function(i){
			return function(){
				alert(i);  
				//这里内部函数的i是外部函数的参数i，由于被引用了，会驻扎在内存中；
				//注意这里的i不是外部循环的i了
			}
		})(i);

	}

	// 闭包需要注意的地方：IE下可能引发内存泄露
	// 如果页面不跳转，变量一直贮存在cpu，只有关闭浏览器的时候，才释放
	// 引发条件：当一个变量，如dom节点 or 数组对象， 它的一个属性（例如onclick）引用一个函数，而这个函数内部又去引用外部这个变量，js对象的属性去引用外部对象，这种循环引用
	var oDiv = document.getElementById("div1");
	oDiv.onclick = function(){
		alert(oDiv.id);
	}

	// 解决方法一：
	window.onunload = function(){
		oDiv.onclick = null;
	}


	// 解决方法二:
	var id = oDiv.id;
	oDiv.onclick = function(){
		alert(id);
	}
	oDiv = null;
}*/



// result.push( function() {alert(item + ' ' + list[i])} );





function buildList(list){
	var result = [];
	for(var i=0; i<list.length; i++) {

		/*修改前：*/
		/* var item = "item" + list[i];
		result.push( function() {alert(item + ' ' + list[i])} );*/

		/*修改方法一：*/
		(function(i){
			var item = "item" + list[i];

			result.push( 
				function() { 
					console.log(item + " " + list[i])
				}
			);
		})(i);

		/*修改方法二：*/
		result.push( 
			(function(i){
				var item = "item" + list[i];
				return function(){
					console.log(item + " " + list[i])
				}
			})(i)
		);
		
	}
	return result;
}

function testList(){
	var fnlist = buildList(["a","b","c"]);
	for(var j=0; j<fnlist.length; j++){
		console.log(j)
		fnlist[j]();
	}
}
testList(); 
//itemc undefined 3次

// 修改后：
//itema a 
//itemb b
//itemc c




function sayAlice() {
    var sayAlert = function() { alert(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return sayAlert;
}
var helloAlice=sayAlice();
helloAlice();

</script>




</body>
</html>